package legoserver;

import java.awt.image.*;
import java.awt.color.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

/**
 * image transformation holds many image manipulations in a hashtable
 */
public class ImageTransformation {
  // create a brand new Hashtable to hold the operations.
  Hashtable mOps = new Hashtable();

  /**
   * constructor creates all effect operations
   */
  public ImageTransformation() {
    createOps();
  }

  /**
   * the createOps() method creates the image processing operations
   */
  public void createOps() {
    // Blurring 3x3
    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
      ninth, ninth, ninth,
      ninth, ninth, ninth,
      ninth, ninth, ninth, };
    mOps.put("Blur 3x3", new ConvolveOp(new Kernel(3, 3, blurKernel)));

    // Blurring 4x4
    float sixteenth = 1.0f / 16.0f;
    float[] mblurKernel = {
      sixteenth, sixteenth, sixteenth, sixteenth,
      sixteenth, sixteenth, sixteenth, sixteenth,
      sixteenth, sixteenth, sixteenth, sixteenth,
      sixteenth, sixteenth, sixteenth, sixteenth };
    mOps.put("Blur 4x4", new ConvolveOp(new Kernel(4, 4, mblurKernel)));

    // Edge detection
    float[] edgeKernel = {
      0.0f, -1.0f, 0.0f,
      -1.0f, 4.0f, -1.0f,
      0.0f, -1.0f, 0.0f };
    mOps.put("Edge detector", new ConvolveOp(new Kernel(3, 3, edgeKernel)));

    // Embossing
    float[] embossKernel = {
      -2.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f,
      0.0f, 0.0f, 2.0f };
    mOps.put("Embossing", new ConvolveOp(new Kernel(3, 3, embossKernel)));

    // Sharpening
    float[] sharpKernel = {
      0.0f, -1.0f, 0.0f,
      -1.0f, 5.0f, -1.0f,
      0.0f, -1.0f, 0.0f };
    mOps.put("Sharpen", new ConvolveOp(new Kernel(3, 3, sharpKernel), ConvolveOp.EDGE_NO_OP, null));

    // Lookup table operations: posterizing and inversion.
    short[] posterize = new short[256];
    short[] invert = new short[256];
    short[] straight = new short[256];
    for (int i = 0; i < 256; i++) {
      posterize[i] = (short)(i - (i % 32));
      invert[i] = (short)(255 - i);
      straight[i] = (short)i;
    }

    mOps.put("Posterize", new LookupOp(new ShortLookupTable(0, posterize),null));
    mOps.put("Invert", new LookupOp(new ShortLookupTable(0, invert), null));
    short[][] blueInvert = new short[][] { straight, straight, invert };
    mOps.put("Invert blue", new LookupOp(new ShortLookupTable(0, blueInvert), null));

    // Thresholding
    mOps.put("Threshold 192", createThresholdOp(192, 0, 255));
    mOps.put("Threshold 128", createThresholdOp(128, 0, 255));
    mOps.put("Threshold 64", createThresholdOp(64, 0, 255));

    // Scaling
    AffineTransform at0 = new AffineTransform();
    at0.scale(0.125f, 0.125f);
    mOps.put("Scale 0.125", new AffineTransformOp(at0, AffineTransformOp.TYPE_BILINEAR));

    AffineTransform at1 = new AffineTransform();
    at1.scale(0.25f, 0.25f);
    mOps.put("Scale 0.25", new AffineTransformOp(at1, AffineTransformOp.TYPE_BILINEAR));

    AffineTransform at2 = new AffineTransform();
    at2.scale(0.5f, 0.5f);
    mOps.put("Scale 0.5", new AffineTransformOp(at2, AffineTransformOp.TYPE_BILINEAR));

    AffineTransform at3 = new AffineTransform();
    at3.scale(2.0f, 2.0f);
    mOps.put("Scale 2.0", new AffineTransformOp(at3, AffineTransformOp.TYPE_BILINEAR));

    // Brightening
    mOps.put("Brighten 25%", new RescaleOp(1.25f, 0, null));
    mOps.put("Brighten 50%", new RescaleOp(1.5f, 0, null));

    // Darkening
    mOps.put("Darken 25%", new RescaleOp(0.75f, 0, null));
    mOps.put("Darken 50%", new RescaleOp(0.5f, 0, null));

    // Color to gray
    ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
    ColorConvertOp op = new ColorConvertOp(cs, null);
    mOps.put("Gray", new ColorConvertOp(cs, null));
  }

  /**
   * createThresholdOp() uses a LookupOp to simulate a thresholding operation.
   * @param threshold value between 0 and 255
   * @param minimum value between 0 and 255
   * @param maximum value between 0 and 255
   * @return BufferedImageOperation
   */
  public BufferedImageOp createThresholdOp(int threshold, int minimum, int maximum) {
    short[] thresholdArray = new short[256];
    for (int i = 0; i < 256; i++) {
      if (i < threshold) { thresholdArray[i] = (short) minimum; }
      else { thresholdArray[i] = (short) maximum; }
    }
    return new LookupOp(new ShortLookupTable(0, thresholdArray), null);
  }

  /**
   * transforms Buffered Image
   * @param key effect operation
   * @param bufferedImage input Buffered Image
   * @return transformed Buffered Image
   */
  public BufferedImage transImage(String key, BufferedImage bufferedImage) {
    BufferedImageOp op = (BufferedImageOp) mOps.get(key);
    return op.filter(bufferedImage, null);
  }
}